
IncludeFile "OpenGL3.pbi"
IncludeFile "mathutils.pb"

;WGL defines to create 3.3 graphics context
#WGL_CONTEXT_MAJOR_VERSION_ARB = $2091
#WGL_CONTEXT_MINOR_VERSION_ARB = $2092
#WGL_CONTEXT_FLAGS_ARB = $2094
#WGL_CONTEXT_PROFILE_MASK_ARB = $9126
#WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = $0002
#WGL_CONTEXT_CORE_PROFILE_BIT_ARB =	$00000001
Prototype.l PFNWGLCREATECONTEXTATTRIBSARBPROC(hDC.l, hShareContext.l, *attribs)

Macro DoubleQuote
"
EndMacro
Macro DefineGLFunction(Func)
	Global Func.PFN#Func#PROC = wglGetProcAddress_(DoubleQuote#Func#DoubleQuote);
	If Func = 0: Debug DoubleQuote#Func#DoubleQuote +" is not linked." : EndIf
EndMacro

Procedure TestGLError()
	error = glGetError_()
	If error <> #GL_NO_ERROR
		Debug "GL error:" + error
	EndIf
EndProcedure

Procedure LinkGLFunctions()
; 	;{ GL_VERSION_1_0
	;Debug "Init OpenGL 1_0"
; 	DefineGLFunction(glCullFace)
; 	DefineGLFunction(glFrontFace)
; 	DefineGLFunction(glHint)
; 	DefineGLFunction(glLineWidth)
; 	DefineGLFunction(glPointSize)
; 	DefineGLFunction(glPolygonMode)
; 	DefineGLFunction(glScissor)
; 	DefineGLFunction(glTexParameterf)
; 	DefineGLFunction(glTexParameterfv)
; 	DefineGLFunction(glTexParameteri)
; 	DefineGLFunction(glTexParameteriv)
; 	DefineGLFunction(glTexImage1D)
; 	DefineGLFunction(glTexImage2D)
; 	DefineGLFunction(glDrawBuffer)
; 	DefineGLFunction(glClear)
; 	DefineGLFunction(glClearColor)
; 	DefineGLFunction(glClearStencil)
; 	DefineGLFunction(glClearDepth)
; 	DefineGLFunction(glStencilMask)
; 	DefineGLFunction(glColorMask)
; 	DefineGLFunction(glDepthMask)
; 	DefineGLFunction(glDisable)
; 	DefineGLFunction(glEnable)
; 	DefineGLFunction(glFinish)
; 	DefineGLFunction(glFlush)
; 	DefineGLFunction(glBlendFunc)
; 	DefineGLFunction(glLogicOp)
; 	DefineGLFunction(glStencilFunc)
; 	DefineGLFunction(glStencilOp)
; 	DefineGLFunction(glDepthFunc)
; 	DefineGLFunction(glPixelStoref)
; 	DefineGLFunction(glPixelStorei)
; 	DefineGLFunction(glReadBuffer)
; 	DefineGLFunction(glReadPixels)
; 	DefineGLFunction(glGetBooleanv)
; 	DefineGLFunction(glGetDoublev)
; 	DefineGLFunction(glGetError)
; 	DefineGLFunction(glGetFloatv)
; 	DefineGLFunction(glGetIntegerv)
; 	DefineGLFunction(glGetString)
; 	DefineGLFunction(glGetTexImage)
; 	DefineGLFunction(glGetTexParameterfv)
; 	DefineGLFunction(glGetTexParameteriv)
; 	DefineGLFunction(glGetTexLevelParameterfv)
; 	DefineGLFunction(glGetTexLevelParameteriv)
; 	DefineGLFunction(glIsEnabled)
; 	DefineGLFunction(glDepthRange)
; 	DefineGLFunction(glViewport)
; 	;}
; 	;{ GL_VERSION_1_1
	;Debug "Init OpenGL 1_1"
	DefineGLFunction(glDrawArrays)
	DefineGLFunction(glDrawElements)
	DefineGLFunction(glGetPointerv)
	DefineGLFunction(glPolygonOffset)
	DefineGLFunction(glCopyTexImage1D)
	DefineGLFunction(glCopyTexImage2D)
	DefineGLFunction(glCopyTexSubImage1D)
	DefineGLFunction(glCopyTexSubImage2D)
	DefineGLFunction(glTexSubImage1D)
	DefineGLFunction(glTexSubImage2D)
	DefineGLFunction(glBindTexture)
	DefineGLFunction(glDeleteTextures)
	DefineGLFunction(glGenTextures)
	DefineGLFunction(glIsTexture)
; 	;}

	;{ GL_VERSION_1_2
	;Debug "Init OpenGL 1_2"
	DefineGLFunction(glDrawRangeElements)
	DefineGLFunction(glTexImage3D)
	DefineGLFunction(glTexSubImage3D)
	DefineGLFunction(glCopyTexSubImage3D)
	;}

	;{ GL_VERSION_1_3
	;Debug "Init OpenGL 1_3"
	DefineGLFunction(glActiveTexture)
	DefineGLFunction(glSampleCoverage)
	DefineGLFunction(glCompressedTexImage3D)
	DefineGLFunction(glCompressedTexImage2D)
	DefineGLFunction(glCompressedTexImage1D)
	DefineGLFunction(glCompressedTexSubImage3D)
	DefineGLFunction(glCompressedTexSubImage2D)
	DefineGLFunction(glCompressedTexSubImage1D)
	DefineGLFunction(glGetCompressedTexImage)
	;}

	;{ GL_VERSION_1_4
	;Debug "Init OpenGL 1_4"
	DefineGLFunction(glBlendFuncSeparate)
	DefineGLFunction(glMultiDrawArrays)
	DefineGLFunction(glMultiDrawElements)
	DefineGLFunction(glPointParameterf)
	DefineGLFunction(glPointParameterfv)
	DefineGLFunction(glPointParameteri)
	DefineGLFunction(glPointParameteriv)
	DefineGLFunction(glBlendColor)
	DefineGLFunction(glBlendEquation)
	;}

	;{ GL_VERSION_1_5
	;Debug "Init OpenGL 1_5"
	DefineGLFunction(glGenQueries)
	DefineGLFunction(glDeleteQueries)
	DefineGLFunction(glIsQuery)
	DefineGLFunction(glBeginQuery)
	DefineGLFunction(glEndQuery)
	DefineGLFunction(glGetQueryiv)
	DefineGLFunction(glGetQueryObjectiv)
	DefineGLFunction(glGetQueryObjectuiv)
	DefineGLFunction(glBindBuffer)
	DefineGLFunction(glDeleteBuffers)
	DefineGLFunction(glGenBuffers)
	DefineGLFunction(glIsBuffer)
	DefineGLFunction(glBufferData)
	DefineGLFunction(glBufferSubData)
	DefineGLFunction(glGetBufferSubData)
	DefineGLFunction(glMapBuffer)
	DefineGLFunction(glUnmapBuffer)
	DefineGLFunction(glGetBufferParameteriv)
	DefineGLFunction(glGetBufferPointerv)
	;}
	
	;{ GL_VERSION_2_0
	;Debug "Init OpenGL 2_0"
	DefineGLFunction(glBlendEquationSeparate)
	DefineGLFunction(glDrawBuffers)
	DefineGLFunction(glStencilOpSeparate)
	DefineGLFunction(glStencilFuncSeparate)
	DefineGLFunction(glStencilMaskSeparate)
	DefineGLFunction(glAttachShader)
	DefineGLFunction(glBindAttribLocation)
	DefineGLFunction(glCompileShader)
	DefineGLFunction(glCreateProgram)
	DefineGLFunction(glCreateShader)
	DefineGLFunction(glDeleteProgram)
	DefineGLFunction(glDeleteShader)
	DefineGLFunction(glDetachShader)
	DefineGLFunction(glDisableVertexAttribArray)
	DefineGLFunction(glEnableVertexAttribArray)
	DefineGLFunction(glGetActiveAttrib)
	DefineGLFunction(glGetActiveUniform)
	DefineGLFunction(glGetAttachedShaders)
	DefineGLFunction(glGetAttribLocation)
	DefineGLFunction(glGetProgramiv)
	DefineGLFunction(glGetProgramInfoLog)
	DefineGLFunction(glGetShaderiv)
	DefineGLFunction(glGetShaderInfoLog)
	DefineGLFunction(glGetShaderSource)
	DefineGLFunction(glGetUniformLocation)
	DefineGLFunction(glGetUniformfv)
	DefineGLFunction(glGetUniformiv)
	DefineGLFunction(glGetVertexAttribdv)
	DefineGLFunction(glGetVertexAttribfv)
	DefineGLFunction(glGetVertexAttribiv)
	DefineGLFunction(glGetVertexAttribPointerv)
	DefineGLFunction(glIsProgram)
	DefineGLFunction(glIsShader)
	DefineGLFunction(glLinkProgram)
	DefineGLFunction(glShaderSource)
	DefineGLFunction(glUseProgram)
	DefineGLFunction(glUniform1f)
	DefineGLFunction(glUniform2f)
	DefineGLFunction(glUniform3f)
	DefineGLFunction(glUniform4f)
	DefineGLFunction(glUniform1i)
	DefineGLFunction(glUniform2i)
	DefineGLFunction(glUniform3i)
	DefineGLFunction(glUniform4i)
	DefineGLFunction(glUniform1fv)
	DefineGLFunction(glUniform2fv)
	DefineGLFunction(glUniform3fv)
	DefineGLFunction(glUniform4fv)
	DefineGLFunction(glUniform1iv)
	DefineGLFunction(glUniform2iv)
	DefineGLFunction(glUniform3iv)
	DefineGLFunction(glUniform4iv)
	DefineGLFunction(glUniformMatrix2fv)
	DefineGLFunction(glUniformMatrix3fv)
	DefineGLFunction(glUniformMatrix4fv)
	DefineGLFunction(glValidateProgram)
	DefineGLFunction(glVertexAttrib1d)
	DefineGLFunction(glVertexAttrib1dv)
	DefineGLFunction(glVertexAttrib1f)
	DefineGLFunction(glVertexAttrib1fv)
	DefineGLFunction(glVertexAttrib1s)
	DefineGLFunction(glVertexAttrib1sv)
	DefineGLFunction(glVertexAttrib2d)
	DefineGLFunction(glVertexAttrib2dv)
	DefineGLFunction(glVertexAttrib2f)
	DefineGLFunction(glVertexAttrib2fv)
	DefineGLFunction(glVertexAttrib2s)
	DefineGLFunction(glVertexAttrib2sv)
	DefineGLFunction(glVertexAttrib3d)
	DefineGLFunction(glVertexAttrib3dv)
	DefineGLFunction(glVertexAttrib3f)
	DefineGLFunction(glVertexAttrib3fv)
	DefineGLFunction(glVertexAttrib3s)
	DefineGLFunction(glVertexAttrib3sv)
	DefineGLFunction(glVertexAttrib4Nbv)
	DefineGLFunction(glVertexAttrib4Niv)
	DefineGLFunction(glVertexAttrib4Nsv)
	DefineGLFunction(glVertexAttrib4Nub)
	DefineGLFunction(glVertexAttrib4Nubv)
	DefineGLFunction(glVertexAttrib4Nuiv)
	DefineGLFunction(glVertexAttrib4Nusv)
	DefineGLFunction(glVertexAttrib4bv)
	DefineGLFunction(glVertexAttrib4d)
	DefineGLFunction(glVertexAttrib4dv)
	DefineGLFunction(glVertexAttrib4f)
	DefineGLFunction(glVertexAttrib4fv)
	DefineGLFunction(glVertexAttrib4iv)
	DefineGLFunction(glVertexAttrib4s)
	DefineGLFunction(glVertexAttrib4sv)
	DefineGLFunction(glVertexAttrib4ubv)
	DefineGLFunction(glVertexAttrib4uiv)
	DefineGLFunction(glVertexAttrib4usv)
	DefineGLFunction(glVertexAttribPointer)
	;}
	;{ GL_VERSION_2_1
	;Debug "Init OpenGL 2_1"
	DefineGLFunction(glUniformMatrix2x3fv)
	DefineGLFunction(glUniformMatrix3x2fv)
	DefineGLFunction(glUniformMatrix2x4fv)
	DefineGLFunction(glUniformMatrix4x2fv)
	DefineGLFunction(glUniformMatrix3x4fv)
	DefineGLFunction(glUniformMatrix4x3fv)
	;}
	;{ GL_VERSION_3_0
	;Debug "Init OpenGL 3_0"
	DefineGLFunction(glColorMaski)
	DefineGLFunction(glGetBooleani_v)
	DefineGLFunction(glGetIntegeri_v)
	DefineGLFunction(glEnablei)
	DefineGLFunction(glDisablei)
	DefineGLFunction(glIsEnabledi)
	DefineGLFunction(glBeginTransformFeedback)
	DefineGLFunction(glEndTransformFeedback)
	DefineGLFunction(glBindBufferRange)
	DefineGLFunction(glBindBufferBase)
	DefineGLFunction(glTransformFeedbackVaryings)
	DefineGLFunction(glGetTransformFeedbackVarying)
	DefineGLFunction(glClampColor)
	DefineGLFunction(glBeginConditionalRender)
	DefineGLFunction(glEndConditionalRender)
	DefineGLFunction(glVertexAttribIPointer)
	DefineGLFunction(glGetVertexAttribIiv)
	DefineGLFunction(glGetVertexAttribIuiv)
	DefineGLFunction(glVertexAttribI1i)
	DefineGLFunction(glVertexAttribI2i)
	DefineGLFunction(glVertexAttribI3i)
	DefineGLFunction(glVertexAttribI4i)
	DefineGLFunction(glVertexAttribI1ui)
	DefineGLFunction(glVertexAttribI2ui)
	DefineGLFunction(glVertexAttribI3ui)
	DefineGLFunction(glVertexAttribI4ui)
	DefineGLFunction(glVertexAttribI1iv)
	DefineGLFunction(glVertexAttribI2iv)
	DefineGLFunction(glVertexAttribI3iv)
	DefineGLFunction(glVertexAttribI4iv)
	DefineGLFunction(glVertexAttribI1uiv)
	DefineGLFunction(glVertexAttribI2uiv)
	DefineGLFunction(glVertexAttribI3uiv)
	DefineGLFunction(glVertexAttribI4uiv)
	DefineGLFunction(glVertexAttribI4bv)
	DefineGLFunction(glVertexAttribI4sv)
	DefineGLFunction(glVertexAttribI4ubv)
	DefineGLFunction(glVertexAttribI4usv)
	DefineGLFunction(glGetUniformuiv)
	DefineGLFunction(glBindFragDataLocation)
	DefineGLFunction(glGetFragDataLocation)
	DefineGLFunction(glUniform1ui)
	DefineGLFunction(glUniform2ui)
	DefineGLFunction(glUniform3ui)
	DefineGLFunction(glUniform4ui)
	DefineGLFunction(glUniform1uiv)
	DefineGLFunction(glUniform2uiv)
	DefineGLFunction(glUniform3uiv)
	DefineGLFunction(glUniform4uiv)
	DefineGLFunction(glTexParameterIiv)
	DefineGLFunction(glTexParameterIuiv)
	DefineGLFunction(glGetTexParameterIiv)
	DefineGLFunction(glGetTexParameterIuiv)
	DefineGLFunction(glClearBufferiv)
	DefineGLFunction(glClearBufferuiv)
	DefineGLFunction(glClearBufferfv)
	DefineGLFunction(glClearBufferfi)
	DefineGLFunction(glGetStringi)
	DefineGLFunction(glIsRenderbuffer)
	DefineGLFunction(glBindRenderbuffer)
	DefineGLFunction(glDeleteRenderbuffers)
	DefineGLFunction(glGenRenderbuffers)
	DefineGLFunction(glRenderbufferStorage)
	DefineGLFunction(glGetRenderbufferParameteriv)
	DefineGLFunction(glIsFramebuffer)
	DefineGLFunction(glBindFramebuffer)
	DefineGLFunction(glDeleteFramebuffers)
	DefineGLFunction(glGenFramebuffers)
	DefineGLFunction(glCheckFramebufferStatus)
	DefineGLFunction(glFramebufferTexture1D)
	DefineGLFunction(glFramebufferTexture2D)
	DefineGLFunction(glFramebufferTexture3D)
	DefineGLFunction(glFramebufferRenderbuffer)
	DefineGLFunction(glGetFramebufferAttachmentParameteriv)
	DefineGLFunction(glGenerateMipmap)
	DefineGLFunction(glBlitFramebuffer)
	DefineGLFunction(glRenderbufferStorageMultisample)
	DefineGLFunction(glFramebufferTextureLayer)
	DefineGLFunction(glMapBufferRange)
	DefineGLFunction(glFlushMappedBufferRange)
	DefineGLFunction(glBindVertexArray)
	DefineGLFunction(glDeleteVertexArrays)
	DefineGLFunction(glGenVertexArrays)
	DefineGLFunction(glIsVertexArray)
	;}
	;{ GL_VERSION_3_1
	;Debug "Init OpenGL 3_1"
	DefineGLFunction(glDrawArraysInstanced)
	DefineGLFunction(glDrawElementsInstanced)
	DefineGLFunction(glTexBuffer)
	DefineGLFunction(glPrimitiveRestartIndex)
	DefineGLFunction(glCopyBufferSubData)
	DefineGLFunction(glGetUniformIndices)
	DefineGLFunction(glGetActiveUniformsiv)
	DefineGLFunction(glGetActiveUniformName)
	DefineGLFunction(glGetUniformBlockIndex)
	DefineGLFunction(glGetActiveUniformBlockiv)
	DefineGLFunction(glGetActiveUniformBlockName)
	DefineGLFunction(glUniformBlockBinding)
	;}
	;{ GL_VERSION_3_2
	;Debug "Init OpenGL 3_2"
	DefineGLFunction(glDrawElementsBaseVertex)
	DefineGLFunction(glDrawRangeElementsBaseVertex)
	DefineGLFunction(glDrawElementsInstancedBaseVertex)
	DefineGLFunction(glMultiDrawElementsBaseVertex)
	DefineGLFunction(glProvokingVertex)
	DefineGLFunction(glFenceSync)
	DefineGLFunction(glIsSync)
	DefineGLFunction(glDeleteSync)
	DefineGLFunction(glClientWaitSync)
	DefineGLFunction(glWaitSync)
	DefineGLFunction(glGetInteger64v)
	DefineGLFunction(glGetSynciv)
	DefineGLFunction(glGetInteger64i_v)
	DefineGLFunction(glGetBufferParameteri64v)
	DefineGLFunction(glFramebufferTexture)
	DefineGLFunction(glTexImage2DMultisample)
	DefineGLFunction(glTexImage3DMultisample)
	DefineGLFunction(glGetMultisamplefv)
	DefineGLFunction(glSampleMaski)
	;}

	;{ GL_ARB_draw_buffers_blend
	DefineGLFunction(glBlendEquationi)
	DefineGLFunction(glBlendEquationSeparatei)
	DefineGLFunction(glBlendFunci)
	DefineGLFunction(glBlendFuncSeparatei)
	;}
	;{ GL_ARB_sample_shading
	DefineGLFunction(glMinSampleShading)
	;}
EndProcedure
	
Procedure DebugExpandedString(string.s)
	Protected.l n, last
	For n = 1 To Len(string)
		If Asc(Mid(string, n, 1)) < 31
			Debug Mid(string, last, n-last)
			last = n
		EndIf
	Next
	If last = 0
		Debug string
	EndIf
EndProcedure

Procedure CreateOpenGLContext(hWnd.l)
	Protected hDC.l, hrc.l
	hDC = GetDC_(hWnd)
	
	;Initialize OpenGL
	Protected pfd.PIXELFORMATDESCRIPTOR
	pfd\nSize				= SizeOf(PIXELFORMATDESCRIPTOR)
	pfd\nVersion		 = 1
	pfd\dwFlags			= #PFD_SUPPORT_OPENGL | #PFD_DOUBLEBUFFER | #PFD_DRAW_TO_WINDOW
	pfd\iLayerType	 = #PFD_MAIN_PLANE
	pfd\iPixelType	 = #PFD_TYPE_RGBA
	pfd\cColorBits	 = 32
	pfd\cDepthBits	 = 24
	
	pixformat = ChoosePixelFormat_(hDC, pfd)
	SetPixelFormat_(hDC, pixformat, pfd)
	hrc = wglCreateContext_(hDC)
	wglMakeCurrent_(hDC, hrc)
	
	Protected wglCreateContextAttribsARB_.PFNWGLCREATECONTEXTATTRIBSARBPROC
	wglCreateContextAttribsARB_ = wglGetProcAddress_("wglCreateContextAttribsARB")
	
	wglMakeCurrent_(0, 0)
	wglDeleteContext_(hrc)
	
	Protected Dim attribs.l(8)
	attribs(0) = #WGL_CONTEXT_MAJOR_VERSION_ARB
	attribs(1) = 3
	attribs(2) = #WGL_CONTEXT_MINOR_VERSION_ARB
	attribs(3) = 3
	attribs(4) = #WGL_CONTEXT_FLAGS_ARB
	attribs(5) = #WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
	attribs(6) = #WGL_CONTEXT_PROFILE_MASK_ARB
	attribs(7) = #WGL_CONTEXT_CORE_PROFILE_BIT_ARB
	attribs(8) = 0
	
	hRC = wglCreateContextAttribsARB_(hDC, 0, @attribs())
	wglMakeCurrent_(hDC, hRC)
	
	LinkGLFunctions()
	
	ProcedureReturn hDC
EndProcedure

Procedure OpenGL_ImageToTexture(ImageNumber, AlphaImageNumber=-1, Filtering=#GL_NEAREST)	
	Protected Counter.l,Color.l,texture
	Protected Dim ImageData.c(ImageWidth(ImageNumber)*ImageHeight(ImageNumber)*4)
	
	StartDrawing(ImageOutput(ImageNumber))
	DrawingMode(#PB_2DDrawing_AllChannels)
	For I = 0 To ImageHeight(ImageNumber)-1
		For J = 0 To ImageWidth(ImageNumber)-1
			color=Point(j,i)
			ImageData(Counter) 		= Red(color)
			ImageData(Counter+1) 	= Green(color)
			ImageData(Counter+2) 	= Blue(color)
			ImageData(Counter+3) 	= Alpha(color)
			Counter+4
		Next
	Next
	bl = DrawingBufferPixelFormat()
	If ((bl & #PB_PixelFormat_32Bits_BGR) Or (bl & #PB_PixelFormat_32Bits_RGB)) And AlphaImageNumber=-1
		AlphaImageNumber = ImageNumber
	EndIf
	StopDrawing()
	
	If AlphaImageNumber
		StartDrawing(ImageOutput(AlphaImageNumber))
		DrawingMode(#PB_2DDrawing_AllChannels)
		counter = 0
		For I = 0 To ImageHeight(ImageNumber)-1
			For J = 0 To ImageWidth(ImageNumber)-1
				color=Point(j,i)
				ImageData(Counter+3)=Alpha(color)
				Counter+4
			Next
		Next
		StopDrawing()
	EndIf
	
	glGenTextures_(1, @texture) 
	glBindTexture_(#GL_TEXTURE_2D, texture) 
	glTexParameteri_(#GL_TEXTURE_2D, #GL_TEXTURE_MIN_FILTER, Filtering)
	glTexParameteri_(#GL_TEXTURE_2D, #GL_TEXTURE_MAG_FILTER, Filtering)
	
	glTexImage2D_(#GL_TEXTURE_2D, 0, #GL_RGBA8, ImageWidth(ImageNumber), ImageHeight(ImageNumber), 0, #GL_RGBA, #GL_UNSIGNED_BYTE, ImageData())
	ProcedureReturn texture	
EndProcedure

Procedure OpenGL_CreateColorRectTexture (format, internalFormat, width, height)
	Protected texture.l

	glGenTextures_(1, @texture )
  glPixelStorei_(#GL_UNPACK_ALIGNMENT, 1 ); set 1-byte alignment
  glBindTexture_(#GL_TEXTURE_RECTANGLE, texture )
  
  If internalFormat = #GL_R32F Or internalFormat = #GL_RGBA32F;TODO
  	glTexImage2D_(#GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0, format, #GL_FLOAT, 0)
  Else
  	glTexImage2D_(#GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0, format, #GL_UNSIGNED_BYTE, 0)
	EndIf

	glTexParameteri_(#GL_TEXTURE_RECTANGLE, #GL_TEXTURE_MAG_FILTER, #GL_NEAREST)
	glTexParameteri_(#GL_TEXTURE_RECTANGLE, #GL_TEXTURE_MIN_FILTER, #GL_NEAREST)
  glTexParameteri_(#GL_TEXTURE_RECTANGLE, #GL_TEXTURE_WRAP_S, #GL_CLAMP_TO_EDGE)
  glTexParameteri_(#GL_TEXTURE_RECTANGLE, #GL_TEXTURE_WRAP_T, #GL_CLAMP_TO_EDGE)

  ProcedureReturn texture
EndProcedure

Procedure.l GetShaderFromFile(shaderID.l, Filename.s)
	Protected String.s, *Memory
	
	Protected file = ReadFile(#PB_Any, Filename)
	If file
		Repeat 
			String=String +" "+ReadString(file) + Chr(#CR)+Chr(#LF)
		Until Eof(file)
		CloseFile(file)
		
		len = Len(String)
		addr = @String

		glShaderSource(shaderID, 1, @addr, @len)
		glCompileShader(shaderID)
		
		result.l
		glGetShaderiv(shaderID, #GL_COMPILE_STATUS, @result)
		If (result = #GL_TRUE)
			ProcedureReturn #True
		Else
			Debug Filename + " failed"
			
			buffer.s = Space(1024)
			length.l = 0
			glGetShaderInfoLog(shaderID, 1024, @length, @buffer);
			
			DebugExpandedString(buffer)
			
			ProcedureReturn #False
		EndIf
		
	EndIf	
EndProcedure
	
Procedure.l LoadShadersFromFiles(vsFilename.s, fsFilename.s, gsFilename.s="")
  Protected shaderProgram	= glCreateProgram()
  
	Protected vertexShader	 = glCreateShader(#GL_VERTEX_SHADER)
	GetShaderFromFile(vertexShader, vsFilename)
	glAttachShader(shaderProgram, vertexShader)
	
	Protected fragmentShader = glCreateShader(#GL_FRAGMENT_SHADER)
	GetShaderFromFile(fragmentShader, fsFilename)
	glAttachShader(shaderProgram, fragmentShader)
	
	If gsFilename <> ""
	  Protected geometryShader = glCreateShader(#GL_GEOMETRY_SHADER)
	  GetShaderFromFile(geometryShader, gsFilename)
	  glAttachShader(shaderProgram, geometryShader)
	EndIf
	
	glLinkProgram(shaderProgram)
	glUseProgram(shaderProgram)
	
	Protected status.l
	glGetProgramiv(shaderProgram, #GL_VALIDATE_STATUS, @status);
	If status = #GL_TRUE
		ProcedureReturn shaderProgram
	Else
		ProcedureReturn 0
	EndIf
EndProcedure



;mappingType = 		#GL_READ_ONNLY, #GL_WRITE_ONLY, or #GL_READ_WRITE
Structure ModelVertex
  pos.vec3
  normal.vec3
  tangent.vec3
  texcoord.vec2
EndStructure

Structure VAO
  ID.l
  VBOindex.l
  VBOvertex.l
  VBOnormals.l
  VBOtangents.l
  VBOtexcoords.l
EndStructure

Enumeration
	#LAYOUT_VERTEX_POS = 1
	#LAYOUT_VERTEX_NORMAL = 2
	#LAYOUT_VERTEX_TEXCOORD = 3
	#LAYOUT_VERTEX_TANGENT = 4
	
	#LAYOUT_VERTEX_ANIMPOS = 6
EndEnumeration

;usage = 		#GL_STREAM_DRAW ,#GL_STREAM_READ, #GL_STREAM_COPY, #GL_STATIC_DRAW, #GL_STATIC_READ, #GL_STATIC_COPY, #GL_DYNAMIC_DRAW, #GL_DYNAMIC_READ, #GL_DYNAMIC_COPY
Procedure CreateVAO(Array vertexArray.ModelVertex(1), Array indexArray.l(1), usage.l)
 *VAO.VAO = AllocateMemory(SizeOf(VAO))
 
 ; create empty VAO and 2 VBOs
 glGenVertexArrays(1, @*VAO\ID)
 glBindVertexArray(*VAO\ID)
 glGenBuffers(1, @*VAO\VBOvertex)
 glGenBuffers(1, @*VAO\VBOindex)
 
 ; fill vertex VBO:
 glBindBuffer(#GL_ARRAY_BUFFER, *VAO\VBOvertex)
 glBufferData(#GL_ARRAY_BUFFER, ArraySize(vertexArray()) * SizeOf(ModelVertex), @vertexArray(), usage)
 
 glVertexAttribPointer(#LAYOUT_VERTEX_POS, 3, #GL_FLOAT, #GL_FALSE, SizeOf(ModelVertex), OffsetOf(ModelVertex\pos));
 glEnableVertexAttribArray(#LAYOUT_VERTEX_POS); positions
 glVertexAttribPointer(#LAYOUT_VERTEX_NORMAL, 3, #GL_FLOAT, #GL_FALSE, SizeOf(ModelVertex), OffsetOf(ModelVertex\normal));
 glEnableVertexAttribArray(#LAYOUT_VERTEX_NORMAL); normals
 glVertexAttribPointer(#LAYOUT_VERTEX_TANGENT, 3, #GL_FLOAT, #GL_FALSE, SizeOf(ModelVertex), OffsetOf(ModelVertex\tangent));
 glEnableVertexAttribArray(#LAYOUT_VERTEX_TANGENT); tangents
 glVertexAttribPointer(#LAYOUT_VERTEX_TEXCOORD, 2, #GL_FLOAT, #GL_FALSE, SizeOf(ModelVertex), OffsetOf(ModelVertex\texcoord));
 glEnableVertexAttribArray(#LAYOUT_VERTEX_TEXCOORD); texture coords
 
 ; fill index VBO:
 glBindBuffer(#GL_ELEMENT_ARRAY_BUFFER, *VAO\VBOindex)
 glBufferData(#GL_ELEMENT_ARRAY_BUFFER, ArraySize(indexArray()) * 4, @indexArray(), usage)
 
 
 ProcedureReturn *VAO
EndProcedure

Procedure.l CreateVAO2(Array vertexArray.vec3(1), Array normalArray.vec3(1), Array tangentArray.vec3(1), Array texcoordArray.vec2(1), Array indexArray.l(1), usage.l)
  *VAO.VAO = AllocateMemory(SizeOf(VAO))

	; create empty VAO
	glGenVertexArrays(1, @*VAO\ID)
	glBindVertexArray(*VAO\ID)
	
	; fill vertex pos VBO:
	If ArraySize(vertexArray()) > 0
	  glGenBuffers(1, @*VAO\VBOvertex)
  	glBindBuffer(#GL_ARRAY_BUFFER, *VAO\VBOvertex)
  	glBufferData(#GL_ARRAY_BUFFER, ArraySize(vertexArray()) * SizeOf(vec3), @vertexArray(), usage)
  	glVertexAttribPointer(#LAYOUT_VERTEX_POS, 3, #GL_FLOAT, #GL_FALSE, SizeOf(vec3), 0);
  	glEnableVertexAttribArray(#LAYOUT_VERTEX_POS)
	EndIf
	; fill vertex normal VBO:
	If ArraySize(normalArray()) > 0
	  glGenBuffers(1, @*VAO\VBOnormals)
  	glBindBuffer(#GL_ARRAY_BUFFER, *VAO\VBOnormals)
  	glBufferData(#GL_ARRAY_BUFFER, ArraySize(normalArray()) * SizeOf(vec3), @normalArray(), usage)
  	glVertexAttribPointer(#LAYOUT_VERTEX_NORMAL, 3, #GL_FLOAT, #GL_FALSE, SizeOf(vec3), 0);
  	glEnableVertexAttribArray(#LAYOUT_VERTEX_NORMAL); normals
  EndIf
  ; fill vertex tangent VBO:
	If ArraySize(tangentArray()) > 0
		glGenBuffers(1, @*VAO\VBOtangents)
		glBindBuffer(#GL_ARRAY_BUFFER, *VAO\VBOtangents)
		glBufferData(#GL_ARRAY_BUFFER, ArraySize(tangentArray()) * SizeOf(vec3), @tangentArray(), usage)
		glVertexAttribPointer(#LAYOUT_VERTEX_TANGENT, 3, #GL_FLOAT, #GL_FALSE, SizeOf(vec3), 0);
		glEnableVertexAttribArray(#LAYOUT_VERTEX_TANGENT)
	EndIf
	; fill vertex texcoord VBO:
	If ArraySize(texcoordArray()) > 0
	  glGenBuffers(1, @*VAO\VBOtexcoords)
  	glBindBuffer(#GL_ARRAY_BUFFER, *VAO\VBOtexcoords)
  	glBufferData(#GL_ARRAY_BUFFER, ArraySize(texcoordArray()) * SizeOf(vec2), @texcoordArray(), usage)
  	glVertexAttribPointer(#LAYOUT_VERTEX_TEXCOORD, 2, #GL_FLOAT, #GL_FALSE, SizeOf(vec2), 0);
  	glEnableVertexAttribArray(#LAYOUT_VERTEX_TEXCOORD); texture coords
  EndIf
  
	; fill index VBO:
	glGenBuffers(1, @*VAO\VBOindex)
	glBindBuffer(#GL_ELEMENT_ARRAY_BUFFER, *VAO\VBOindex)
	glBufferData(#GL_ELEMENT_ARRAY_BUFFER, ArraySize(indexArray()) * 4, @indexArray(), usage)
	
	
	ProcedureReturn *VAO
EndProcedure

Procedure CleanVAO(*VAO.VAO)
  If glIsVertexArray(*VAO\ID)
    glBindVertexArray(*VAO\ID)
  
    If glIsBuffer(*VAO\VBOindex)
      glDeleteBuffers(1, @*VAO\VBOindex)
    EndIf
    
    If glIsBuffer(*VAO\VBOvertex)
      glDeleteBuffers(1, @*VAO\VBOvertex)
    EndIf
    
    If glIsBuffer(*VAO\VBOnormals)
      glDeleteBuffers(1, @*VAO\VBOnormals)
    EndIf
    
    If glIsBuffer(*VAO\VBOtexcoords)
      glDeleteBuffers(1, @*VAO\VBOtexcoords)
    EndIf
  EndIf
  
  FreeMemory(*VAO)
EndProcedure



;Mesh structure
Structure MeshMaterial
	TextureName.s
	ShaderProgramID.l
EndStructure

Structure VAOSubMesh
	Mat.MeshMaterial
	
  StartPosition.l
  NumOfIndices.l
  NumOfVertices.l
  
  VAO.VAO
EndStructure

Structure Mesh
	BBoxMin.vec3
	BBoxMax.vec3
	
	DefaultMaterial.MeshMaterial ;default material for all submeshes
  Array SubMeshes.VAOSubMesh(0)
EndStructure

Structure AnimatedMesh Extends Mesh
	NumOfFrames.l
EndStructure


;Mesh loaders need to be here
IncludeFile "procedural_geometry.pb"
IncludeFile "md3.pb"

Procedure BindMaterial(*mat.MeshMaterial)
	
	
EndProcedure
	
Procedure DrawMesh(*Mesh.mesh)
	For i = 0 To ArraySize(*mesh\SubMeshes())		
		glBindVertexArray(*mesh\SubMeshes(i)\VAO\ID)
		glDrawElements(#GL_TRIANGLES, *mesh\SubMeshes(i)\NumOfIndices, #GL_UNSIGNED_INT, 0)
  Next
EndProcedure

Procedure DrawAnimatedMesh(*Mesh.AnimatedMesh, frame1, frame2, t.f)
	If frame1>=frame2
		Swap frame1, frame2
		t = 1.0-t
	EndIf
	
	If (frame2 >= *Mesh\NumOfFrames); Or (frame1 >= *Mesh\SubMeshes(i)\NumOfFrames)
		ProcedureReturn
	EndIf
	
	For i = 0 To ArraySize(*mesh\SubMeshes())
		glBindVertexArray(*mesh\SubMeshes(i)\VAO\ID) ;we need to fix start positions of attributes
		
  	glBindBuffer(#GL_ARRAY_BUFFER, *mesh\SubMeshes(i)\VAO\VBOvertex)
  	
  	glEnableVertexAttribArray(#LAYOUT_VERTEX_POS)
  	glVertexAttribPointer(#LAYOUT_VERTEX_POS, 3, #GL_FLOAT, #GL_FALSE, SizeOf(vec3), *mesh\SubMeshes(i)\NumOfVertices * frame1 * SizeOf(vec3));
  	glEnableVertexAttribArray(#LAYOUT_VERTEX_ANIMPOS)
  	glVertexAttribPointer(#LAYOUT_VERTEX_ANIMPOS, 3, #GL_FLOAT, #GL_FALSE, SizeOf(vec3), *mesh\SubMeshes(i)\NumOfVertices * frame2 * SizeOf(vec3));
  	
		glDrawElements(#GL_TRIANGLES, *mesh\SubMeshes(i)\NumOfIndices, #GL_UNSIGNED_INT, 0)
  Next
EndProcedure





















; IDE Options = PureBasic 5.11 (Windows - x86)
; CursorPosition = 748
; FirstLine = 556
; Folding = EAgR9
; EnableXP